//package com.itrip.log.module.db.mybatis;
//
//import io.protostuff.LinkedBuffer;
//import io.protostuff.ProtostuffIOUtil;
//import io.protostuff.Schema;
//import io.protostuff.runtime.RuntimeSchema;
//
//import java.util.Collection;
//import java.util.HashSet;
//import java.util.List;
//import java.util.Map;
//import java.util.Properties;
//import java.util.Set;
//
//import org.apache.ibatis.executor.Executor;
//import org.apache.ibatis.mapping.BoundSql;
//import org.apache.ibatis.mapping.MappedStatement;
//import org.apache.ibatis.mapping.ParameterMapping;
//import org.apache.ibatis.mapping.SqlCommandType;
//import org.apache.ibatis.plugin.Interceptor;
//import org.apache.ibatis.plugin.Intercepts;
//import org.apache.ibatis.plugin.Invocation;
//import org.apache.ibatis.plugin.Plugin;
//import org.apache.ibatis.plugin.Signature;
//import org.apache.ibatis.reflection.MetaObject;
//import org.apache.ibatis.session.Configuration;
//import org.apache.ibatis.session.ResultHandler;
//import org.apache.ibatis.session.RowBounds;
//import org.apache.ibatis.type.TypeHandlerRegistry;
//import org.slf4j.Logger;
//import org.slf4j.LoggerFactory;
//import org.webbitserver.dependencies.org.jboss.netty.util.internal.ConcurrentHashMap;
//
//import com.alibaba.druid.sql.ast.SQLStatement;
//import com.alibaba.druid.sql.ast.statement.SQLDeleteStatement;
//import com.alibaba.druid.sql.ast.statement.SQLInsertStatement;
//import com.alibaba.druid.sql.ast.statement.SQLUpdateStatement;
//import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser;
//import com.alibaba.druid.sql.parser.SQLStatementParser;
//import com.alibaba.druid.sql.visitor.SchemaStatVisitor;
//import com.alibaba.druid.stat.TableStat.Name;
//
//@Intercepts({
//		@Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }),
//		@Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class,
//				RowBounds.class, ResultHandler.class }) })
//public class CopyOfCacheInterceptor implements Interceptor {
//
//	/***
//	 * 缓存的对象实体
//	 */
//	public static class CacheItem {
//		/** 要缓存的数据 */
//		Object data;
//	}
//
//	public static interface DeepCopy {
//		Object copy();
//	}
//
//	private String showType;
//	private Properties properties;
//	private boolean showAll = false;
//	private static final Logger LOG = LoggerFactory.getLogger(CopyOfCacheInterceptor.class);
//	private static final Schema<CacheItem> SCHEMA = RuntimeSchema.getSchema(CacheItem.class);
//
//	private ConcurrentHashMap<Object, Object> CAHCE = new ConcurrentHashMap<Object, Object>();
//	/** 记录table关联哪些缓存,在这些表执行更新删除插入时清除缓存 key:table value:CAHCE.key */
//	private ConcurrentHashMap<String, Set<String>> TABLE_SQL = new ConcurrentHashMap<String, Set<String>>();
//
//	public Object intercept(Invocation invocation) throws Throwable {
////		long start = System.currentTimeMillis();
////		Object[] args = invocation.getArgs();
////		MappedStatement mst = (MappedStatement) args[0];
////		BoundSql boundSql = mst.getBoundSql(args[1]);
////		String sql = showSql(mst, boundSql);
////		Object data = doCache(invocation, mst, sql);
////		doPrintSQLWithParams(start, mst, sql);
//		return invocation.proceed();
//	}
//
//	private Object doCache(Invocation invocation, MappedStatement mst, String sql) throws Exception {
//		if (!mst.isUseCache())
//			return invocation.proceed();
//		if (mst.getSqlCommandType() == SqlCommandType.SELECT)
//			return doSelect(invocation, sql);
//		Object obj = invocation.proceed();
//		removeTableRelatedCache(sql);
//		return obj;
//	}
//
//	private void removeTableRelatedCache(String sql) {
//		for (String table : parseTables(sql)) {
//			Set<String> querySql = TABLE_SQL.remove(table);
//			if (querySql == null)
//				continue;
//			for (String item : querySql) {
//				CAHCE.remove(item);
//			}
//			LOG.info("remove cache when table:{} update", table);
//		}
//	}
//
//	private Object doSelect(Invocation invocation, String sql) throws Exception {
//		Object data = CAHCE.get(sql);
//		CacheItem item = new CacheItem();
//		if (data == null) {
//			Object obj = invocation.proceed();
//			if (obj == null)
//				return obj;
//			if (isNotSeriaClass(obj)) {
//				return CAHCE.put(sql, obj);
//			}
//			for (String table : parseSelectTables(sql)) {
//				Set<String> set = TABLE_SQL.get(table);
//				if (set == null) {
//					TABLE_SQL.putIfAbsent(table, new HashSet<String>());
//				}
//				TABLE_SQL.get(table).add(sql);
//			}
//			item.data = obj;
//			LinkedBuffer allocate = LinkedBuffer.allocate(256);
//			CAHCE.put(sql, ProtostuffIOUtil.toByteArray(item, SCHEMA, allocate));
//			return obj;
//		} else if (data instanceof byte[]) {
//			ProtostuffIOUtil.mergeFrom((byte[]) data, item, SCHEMA);
//		} else if (data instanceof DeepCopy) {
//			item.data = ((DeepCopy) data).copy();
//		}
//		LOG.info("hit cache:{}", sql);
//		return item.data;
//	}
//
//	/***
//	 * 判断是否需要序列化,不序列化的对象直接put
//	 * 
//	 * @param data
//	 * @return
//	 */
//	private boolean isNotSeriaClass(Object data) {
//		Class<?> cls = data.getClass();
//		return cls.isPrimitive() || cls == DeepCopy.class || (cls == Map.class && ((Map<?, ?>) data).isEmpty())
//				|| (cls == Collection.class && ((Collection<?>) data).isEmpty())
//				|| (cls == Object[].class && ((Object[]) data).length == 0);
//	}
//
//	/**
//	 * 获取sql关联的table的name
//	 * 
//	 * @param regx
//	 * @param srcSql
//	 * @return
//	 */
//	private Set<String> parseTables(String srcSql) {
//		Set<String> tables = new HashSet<String>();
//		SQLStatementParser parser = new MySqlStatementParser(srcSql);
//		for (SQLStatement stmt : parser.parseStatementList()) {
//			if (stmt instanceof SQLInsertStatement) {
//				SQLInsertStatement tmp = (SQLInsertStatement) stmt;
//				tables.add(tmp.getTableName().getSimpleName());
//			} else if (stmt instanceof SQLUpdateStatement) {
//				SQLUpdateStatement tmp = (SQLUpdateStatement) stmt;
//				tables.add(tmp.getTableName().getSimpleName());
//			} else if (stmt instanceof SQLDeleteStatement) {
//				SQLDeleteStatement tmp = (SQLDeleteStatement) stmt;
//				tables.add(tmp.getTableName().getSimpleName());
//			}
//		}
//		return tables;
//	}
//
//	private Set<String> parseSelectTables(String srcSql) {
//		Set<String> tables = new HashSet<String>();
//		SchemaStatVisitor c = new SchemaStatVisitor();
//		SQLStatementParser parser = new MySqlStatementParser(srcSql);
//		for (SQLStatement stmt : parser.parseStatementList()) {
//			stmt.accept(c);
//			for (Name name : c.getTables().keySet()) {
//				tables.add(name.getName().toLowerCase());
//			}
//		}
//		return tables;
//	}
//
//	private void doPrintSQLWithParams(long start, MappedStatement mst, String sql) {
//		if (showAll || showType.contains(mst.getSqlCommandType().name())) {
//			long end = (System.currentTimeMillis() - start);
//			LOG.info("[time:{}ms][id:{}][sql:{}] ", end, mst.getId(), sql);
//		}
//	}
//
//	private String showSql(MappedStatement mst, BoundSql bound) {
//		Configuration cfg = mst.getConfiguration();
//		String sql = bound.getSql().replaceAll("\\s+", " ");
//		// 如果不是SELECT 并且也不需要打印SQL则无需替换参数
//		SqlCommandType type = mst.getSqlCommandType();
//		if (type != SqlCommandType.SELECT && (showAll || showType.contains(type.name()))) {
//			return sql;
//		}
//		Object pObject = bound.getParameterObject();
//		List<ParameterMapping> mappings = bound.getParameterMappings();
//		if (mappings.size() > 0 && pObject != null) {
//			TypeHandlerRegistry registry = cfg.getTypeHandlerRegistry();
//			if (registry.hasTypeHandler(pObject.getClass())) {
//				return sql.replaceFirst("\\?", String.format("'%s'", pObject));
//			}
//
//			MetaObject meta = cfg.newMetaObject(pObject);
//			for (ParameterMapping param : mappings) {
//				String pname = param.getProperty();
//				if (meta.hasGetter(pname)) {
//					Object value = meta.getValue(pname);
//					sql = sql.replaceFirst("\\?", String.format("'%s'", value));
//				} else if (bound.hasAdditionalParameter(pname)) {
//					Object obj = bound.getAdditionalParameter(pname);
//					sql = sql.replaceFirst("\\?", String.format("'%s'", obj));
//				}
//			}
//		}
//		return sql;
//	}
//
//	public Object plugin(Object target) {
//		return Plugin.wrap(target, this);
//	}
//
//	public void setProperties(Properties prop) {
//		showType = prop.getProperty("showSqlType", "ALL").toUpperCase();
//		showAll = showType.equals("ALL");
//		properties = prop;
//	}
//
//	public Properties getProperties() {
//		return properties;
//	}
//}
